﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Optimizer
{
    /// <summary>
    /// Локальный поиск, синтез случайного поиска с обучением и метода
    /// Розенброка (вращающихся координат).
    /// </summary>
    class MixedNew: IOptimizer
    {
        public string Name()
        {
            return "Синтез";
        }

        double StepLen = 1; //длина шага
        double Kappa = 0.618;
        double Eps = 0.001; //точность
        int N = 20; //кол-во проб
        double F, FPr;
        Point XPr, XNew;
        const int M = 5;//кол-во строк "обучающей" матрицы
        FPoint[] PreHistory = new FPoint[M];
        
        Point X0 = new Point()
        {
            X1 = -1,
            X2 = 1,
        };

        Point[] A = new Point[2];
        Point[] B = new Point[2];
        Point[] NB = new Point[2];

        Point Lambda = new Point()
        {
            X1 = 2,
            X2 = 0.5,
        };

        private Random R = new Random();

        private double NextRandom()
        {
            return 2 * this.R.NextDouble() - 1;
        }

        private Point NextRandomPoint()
        {
            Point Temp = new Point()
            {
                X1 = NextRandom(),
                X2 = NextRandom(),
            };
            return Temp;
        }

        private Point DeltaX()
        {
            return StepLen * NextRandomPoint();
        }

        private void FormCenter(Func<Point, double> f)
        {
            //значение в начальной точке
            F = f(X0);
            // координаты пробного центра
            XPr = X0 + DeltaX();
            FPr = f(XPr);
            while (FPr < F)
            {
                X0 = XPr;
                F = FPr;
                XPr = X0 + DeltaX();
                FPr = f(XPr);
            }
        }

        private void FormMatrix(Func<Point, double> f)
        {
            for (int i = 0; i < M - 1; i++)
            {
                PreHistory[i] = new FPoint()
                {
                    X = X0,
                    Y = double.MaxValue,
                };
            }
            PreHistory[M - 1] = new FPoint()
            {
                X = X0,
                Y = F,
            };
        }

        private FPoint StudyBlock(Func<Point, double> f)
        {
            XPr = PreHistory[M - 1].X + DeltaX();
            FPoint Min = new FPoint()
            {
                X = XPr,
                Y = f(XPr),
            };

            for (int i = 1; i < N; i++)
            {
                XPr = PreHistory[M - 1].X + DeltaX();
                FPr = f(XPr);
                if (FPr < Min.Y)
                {
                    Min.X = XPr;
                    Min.Y = FPr;
                }
            }
            return Min;
        }

        private void CorrectMatrix(Point aX, double aPhi)
        {
            if (aPhi < PreHistory[0].Y)
            {
                PreHistory[0].X = aX;
                PreHistory[0].Y = aPhi;
                for (int j = 1; j < M; j++)
                {
                    if (aPhi < PreHistory[j].Y)
                    {
                        PreHistory[j - 1] = PreHistory[j];
                        PreHistory[j].X = aX;
                        PreHistory[j].Y = aPhi;
                    }
                }
            }
        }

        private void Ort()
        {
            A[1] = A[0];
            A[1].X1 = 0;

            NB[0] = A[0].Norm();

            B[1] = A[1] - NB[0] * (A[1] * NB[0]);
            NB[1] = B[1].Norm();
        }

        public Point Do(Func<Point, double> f)
        {
            FormCenter(f);
            FormMatrix(f);
            int StartNum = 0;
            while (StartNum < M)
            {
                FPoint StudyF = StudyBlock(f);
                if (StudyF.Y < PreHistory[0].Y) StartNum++;
                CorrectMatrix(StudyF.X, StudyF.Y);
            }

            f(X0);

            X0 = PreHistory[M - 1].X;
            F = PreHistory[M - 1].Y;
            A[0]= X0 - PreHistory[M-2].X;

            Ort();

            do
            {
                bool flag = false;

                XNew = X0;
                XPr = XNew + StepLen *  NB[0];
                FPr = f(XPr);
                if (FPr < F)
                {
                    XNew = XPr;
                    F = FPr;
                    flag = true;
                }
                else
                {
                    XPr = XNew - StepLen * NB[0];
                    FPr = f(XPr);
                    if (FPr < F)
                    {
                        XNew = XPr;
                        F = FPr;
                        flag = true;
                    }
                }

                XPr = XNew + StepLen * NB[1] * 0.2;
                FPr = f(XPr);
                if (FPr < F)
                {
                    XNew = XPr;
                    F = FPr;
                    flag = true;
                }
                else
                {
                    XPr = XNew - StepLen *NB[1]* 0.2;
                    FPr = f(XPr);
                    if (FPr < F)
                    {
                        XNew = XPr;
                        F = FPr;
                        flag = true;                        
                    }
                }

                if (flag)
                {
                    A[0] = XNew - X0;
                    X0 = XNew;
                    StepLen = StepLen / Kappa;
                    Ort();
                }
                else StepLen = StepLen * Kappa;
            } while (StepLen > Eps);
            return X0;
        }
    }
}
